#import <AddressBook/AddressBook.h>
#import <UIKit/UIKit.h>
#import "RCTContacts.h"
#import <AssetsLibrary/AssetsLibrary.h>
#import "RCTContactsWrapper.h"
#import "NSString+Pinyin.h"

#define SuppressPerformSelectorLeakWarning(Stuff) \
do { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
} while (0)

@interface PersonObject : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSDictionary *dict;
+ (instancetype)modelWithDictionary:(NSDictionary *)dict;
@end

@implementation PersonObject
+ (instancetype)modelWithDictionary:(NSDictionary *)dict {
    PersonObject *model = [PersonObject new];
    if ([dict isKindOfClass:[NSDictionary class]]) {
        model.dict = dict;
        model.name = [NSString stringWithFormat:@"%@%@", dict[@"familyName"], dict[@"givenName"]];
    } else if ([dict isKindOfClass:[NSString class]]) {
        model.name = (NSString *)dict;
    }
    return model;
}
@end

@interface RCTContacts()

@property(nonatomic, retain) RCTContactsWrapper *contactsWrapper;
@property(nonatomic, copy) RCTResponseSenderBlock callBack;

@end

@implementation RCTContacts {
    CNContactStore * contactStore;
    ABAddressBookRef bookRef;
}

- (void)dealloc {
    if (bookRef) {
        CFRelease(bookRef);
    }
}

-(RCTContactsWrapper *)contactsWrapper {
    if (!_contactsWrapper) {
        _contactsWrapper = [RCTContactsWrapper new];
    }
    
    return _contactsWrapper;
}

RCT_EXPORT_MODULE();

- (NSDictionary *)constantsToExport
{
    return @{
             @"PERMISSION_DENIED": @"denied",
             @"PERMISSION_AUTHORIZED": @"authorized",
             @"PERMISSION_UNDEFINED": @"undefined"
             };
}

RCT_EXPORT_METHOD(checkPermission:(RCTResponseSenderBlock) callback)
{
    if([CNContact class]) {
        //iOS 9+
        CNAuthorizationStatus authStatus = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
        if (authStatus == CNAuthorizationStatusDenied || authStatus == CNAuthorizationStatusRestricted){
            callback(@[[NSNull null], @"denied"]);
        } else if (authStatus == CNAuthorizationStatusAuthorized){
            callback(@[[NSNull null], @"authorized"]);
        } else {
            callback(@[[NSNull null], @"undefined"]);
        }
    } else {
        //iOS 8 and below
        if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
        {
            callback(@[[NSNull null], @"denied"]);
        } else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized)
        {
            callback(@[[NSNull null], @"authorized"]);
        } else {
            callback(@[[NSNull null], @"undefined"]);
        }
    }
}

RCT_EXPORT_METHOD(requestPermission:(RCTResponseSenderBlock) callback)
{
    if([CNContact class]) {
        //iOS 9+
        CNContactStore* contactStore = [[CNContactStore alloc] init];
        
        [contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
            [self checkPermission:callback];
        }];
    } else {
        //iOS 8 and below
        ABAddressBookRef bookRef = ABAddressBookCreate();
        ABAddressBookRequestAccessWithCompletion(bookRef, ^(bool granted, CFErrorRef error) {
            [self checkPermission:callback];
        });
    }
}

RCT_EXPORT_METHOD(getContactsMatchingString:(NSString *)string callback:(RCTResponseSenderBlock) callback)
{
    if([CNContact class]) {
        //iOS 9+
        CNContactStore *contactStore = [[CNContactStore alloc] init];
        if (!contactStore)
            return;
        [self getContactsFromAddressBook:contactStore matchingString:string callback:callback];
    } else {
        //iOS 8 and below
        // 创建通讯录对象
        ABAddressBookRef aBookRef = ABAddressBookCreate();
        if (!aBookRef)
            return;
        [self getABRecordRefContactsFromAddressBook:aBookRef matchingString:string callback:callback];
        CFRelease(aBookRef);
    }
}

-(void) getContactsFromAddressBook:(CNContactStore *)store
                    matchingString:(NSString *)searchString
                       callback:(RCTResponseSenderBlock)callback
{
    NSMutableArray *contacts = [[NSMutableArray alloc] init];
    NSError *contactError = nil;
    NSArray *keys = @[
                      CNContactEmailAddressesKey,
                      CNContactPhoneNumbersKey,
                      CNContactFamilyNameKey,
                      CNContactGivenNameKey,
                      CNContactMiddleNameKey,
                      CNContactPostalAddressesKey,
                      CNContactOrganizationNameKey,
                      CNContactJobTitleKey,
                      CNContactImageDataAvailableKey
                      ];
    NSArray *arrayOfContacts = [store unifiedContactsMatchingPredicate:[CNContact predicateForContactsMatchingName:searchString]
                                                           keysToFetch:keys
                                                                 error:&contactError];
    [arrayOfContacts enumerateObjectsUsingBlock:^(CNContact * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSDictionary *contactDictionary = [self contactToDictionary:obj withThumbnails:NO];
        [contacts addObject:contactDictionary];
    }];
    //callback(@[[NSNull null], contacts]);
    callback(@[[NSNull null], [self sortedFamilyNameArray:contacts]]);
}

-(void) getABRecordRefContactsFromAddressBook:(ABAddressBookRef) addressBook
                    matchingString:(NSString *)searchString
                          callback:(RCTResponseSenderBlock)callback
{
    NSMutableArray *contacts = [[NSMutableArray alloc] init];
    // 获取通讯录中匹配到的联系人
    CFArrayRef arrayRef = ABAddressBookCopyPeopleWithName(addressBook, (__bridge CFStringRef)(searchString));
    NSArray *arrayOfContacts = (__bridge NSArray *)arrayRef;
    [arrayOfContacts enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSDictionary *contactDictionary = [self contactABRecordRefToDictionary:(__bridge ABRecordRef)(obj) withThumbnails:NO];
        [contacts addObject:contactDictionary];
    }];
    //callback(@[[NSNull null], contacts]);
    callback(@[[NSNull null], [self sortedFamilyNameArray:contacts]]);
}

-(void) getAllContacts:(RCTResponseSenderBlock) callback
        withThumbnails:(BOOL) withThumbnails
{
    if([CNContact class]) {
        //iOS 9+
        CNContactStore* contactStore = [self contactsStore:callback];
        if(!contactStore)
            return;
        
        [self retrieveContactsFromAddressBook:contactStore withThumbnails:withThumbnails withCallback:callback];
    } else {
        //iOS 8 and below
        [self getAllABRecordRefContacts:callback withThumbnails:withThumbnails];
    }
}

-(void) getAllABRecordRefContacts:(RCTResponseSenderBlock) callback
        withThumbnails:(BOOL) withThumbnails
{
    ABAddressBookRef bookRef = [self bookRef:callback];
    if(!bookRef)
        return;
    
    [self retrieveABRecordRefContactsFromAddressBook:bookRef withThumbnails:withThumbnails withCallback:callback];
}

RCT_EXPORT_METHOD(getAll:(RCTResponseSenderBlock) callback)
{
    [self getAllContacts:callback withThumbnails:true];
}

RCT_EXPORT_METHOD(getAllWithoutPhotos:(RCTResponseSenderBlock) callback)
{
    [self getAllContacts:callback withThumbnails:false];
}

// UILocalizedIndexedCollation -- 本地化下按首字母分组排序
- (NSMutableArray *)setObjects:(NSArray *)objects {
    SEL selector = @selector(name);
    UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation];
    
    // 得到collation索引数量（26个字母和1个#）
    NSInteger sectionTitlesCount = [[collation sectionTitles] count];
    
    // 初始化数组mutableSections用来存放最终数据，我们最终要得到的数据模型应该形如 \
    @[@[以A开头的数据数组], @[以B开头的数据数组], ... @[以#(其它)开头的数据数组]]
    NSMutableArray *mutableSections = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount];
    
    // 初始化27个空数组加入mutableSections
    for (NSInteger idx = 0; idx < sectionTitlesCount; idx++) {
        [mutableSections addObject:[NSMutableArray array]];
    }
    
    // 将每个人按name分到某个section下
    for (id obj in objects) {
        // 获取name属性的值所在的位置，比如“林”首字母是L，在A~Z中排第11，sectionNumber则为11
        NSInteger sectionNumber = [collation sectionForObject:obj collationStringSelector:selector];
        // 通过 performSelector: 来调用对象的方法
        SuppressPerformSelectorLeakWarning(
           NSString *name = [obj performSelector:selector];
           if (name.length > 0 && isFirstLetterHANZI([name characterAtIndex:0])) {
               NSString *firstLetters = [name firstLettersForSort:YES];
               if (firstLetters.length > 0) {
                   NSInteger tempSectionNumber = [[[UILocalizedIndexedCollation currentCollation] sectionTitles] indexOfObject:[[firstLetters substringToIndex:1] lowercaseString]];
                   if (tempSectionNumber == NSNotFound) {
                       tempSectionNumber = [[[UILocalizedIndexedCollation currentCollation] sectionTitles] indexOfObject:[[firstLetters substringToIndex:1] uppercaseString]];
                   }
                   if (tempSectionNumber != NSNotFound) {
                       sectionNumber = tempSectionNumber;
                   }
               }
           }
        );
        
        // name为“林”的obj则加入mutableSections中的第11个数组中
        [[mutableSections objectAtIndex:sectionNumber] addObject:obj];
    }
    
    // 对每个section中的数组按照name属性排序
    for (NSUInteger idx = 0; idx < sectionTitlesCount; idx++) {
        NSArray *objsForSection = [mutableSections objectAtIndex:idx];
        [mutableSections replaceObjectAtIndex:idx withObject:[collation sortedArrayFromArray:objsForSection collationStringSelector:selector]];
    }
    return mutableSections;
}

- (NSArray *)sortedFamilyNameArray:(NSArray *)objects {
    
    NSMutableArray *willDeleObjs = @[].mutableCopy;
    NSMutableArray *reservedObjs = @[].mutableCopy;
    NSMutableArray *reservedSectionTitles = @[].mutableCopy;
    
    NSMutableArray *dataSources = [NSMutableArray array];
    for (id obj in objects) {
        [dataSources addObject:[PersonObject modelWithDictionary:obj]];
    }
    
    NSMutableArray *newSectionsArray = [self setObjects:dataSources];
    [newSectionsArray enumerateObjectsUsingBlock:^(NSArray *arr, NSUInteger idx, BOOL *stop) {
        if (arr.count == 0) {
            [willDeleObjs addObject:arr];
        } else {
            // sectionTitles forexample: 'G'
            // [[UILocalizedIndexedCollation currentCollation] sectionTitles][idx]
            NSMutableArray *mArr = [arr mutableCopy];
            [mArr enumerateObjectsUsingBlock:^(PersonObject *obj, NSUInteger index, BOOL *stop) {
                mArr[index] = obj.dict;
            }];
            [reservedSectionTitles addObject:[[UILocalizedIndexedCollation currentCollation] sectionTitles][idx]];
            [reservedObjs addObject:mArr];
        }
    }];
    // 我们可以去除掉mutableSections中空的数组
    [newSectionsArray removeObjectsInArray:willDeleObjs];
    
    // NSLog(@"reservedObjs - %@", reservedObjs);
    
    // 打印出排序后的name
    NSMutableArray *resultArray = [NSMutableArray new];
    [reservedObjs enumerateObjectsUsingBlock:^(NSArray *arr, NSUInteger idx, BOOL * _Nonnull stop) {
        [resultArray addObjectsFromArray:arr];
    }];
    
    return [resultArray copy];
}

-(void) retrieveABRecordRefContactsFromAddressBook:(ABAddressBookRef) addressBook
                         withThumbnails:(BOOL) withThumbnails
                           withCallback:(RCTResponseSenderBlock) callback
{
    NSMutableArray *contacts = [[NSMutableArray alloc] init];
    // 获取通讯录中所有的联系人
    CFArrayRef arrayRef = ABAddressBookCopyArrayOfAllPeople(addressBook);
    NSArray *arrayOfContacts = (__bridge NSArray *)arrayRef;
    [arrayOfContacts enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSDictionary *contactDictionary = [self contactABRecordRefToDictionary:(__bridge ABRecordRef)(obj) withThumbnails:NO];
        [contacts addObject:contactDictionary];
    }];
    
    //callback(@[[NSNull null], contacts]);
    callback(@[[NSNull null], [self sortedFamilyNameArray:contacts]]);
}

-(void) retrieveContactsFromAddressBook:(CNContactStore*)contactStore
                         withThumbnails:(BOOL) withThumbnails
                           withCallback:(RCTResponseSenderBlock) callback
{
    NSMutableArray *contacts = [[NSMutableArray alloc] init];
    
    NSError* contactError;
    [contactStore containersMatchingPredicate:[CNContainer predicateForContainersWithIdentifiers: @[contactStore.defaultContainerIdentifier]] error:&contactError];
    
    
    NSMutableArray *keysToFetch = [[NSMutableArray alloc]init];
    [keysToFetch addObjectsFromArray:@[
                                       CNContactEmailAddressesKey,
                                       CNContactPhoneNumbersKey,
                                       CNContactFamilyNameKey,
                                       CNContactGivenNameKey,
                                       CNContactMiddleNameKey,
                                       CNContactPostalAddressesKey,
                                       CNContactOrganizationNameKey,
                                       CNContactJobTitleKey,
                                       CNContactImageDataAvailableKey
                                       ]];
    
    if(withThumbnails) {
        [keysToFetch addObject:CNContactThumbnailImageDataKey];
    }
    
    CNContactFetchRequest * request = [[CNContactFetchRequest alloc]initWithKeysToFetch:keysToFetch];
    BOOL success = [contactStore enumerateContactsWithFetchRequest:request error:&contactError usingBlock:^(CNContact * __nonnull contact, BOOL * __nonnull stop){
        NSDictionary *contactDict = [self contactToDictionary: contact withThumbnails:withThumbnails];
        [contacts addObject:contactDict];
    }];
    
    //callback(@[[NSNull null], contacts]);
    callback(@[[NSNull null], [self sortedFamilyNameArray:contacts]]);
}

-(CNContact *)getContactFromAddressBook:(CNContactStore*)contactStore identifier:(NSString *)identifier {
    if (!contactStore || !identifier) {
        return nil;
    }
    
    __block CNContact *findContact = nil;
    NSError *contactError;
    [contactStore containersMatchingPredicate:[CNContainer predicateForContainersWithIdentifiers: @[contactStore.defaultContainerIdentifier]] error:&contactError];
    
    NSMutableArray *keysToFetch = [[NSMutableArray alloc]init];
    [keysToFetch addObjectsFromArray:@[
                                       CNContactGivenNameKey,
                                       CNContactMiddleNameKey,
                                       CNContactFamilyNameKey,
                                       CNContactPreviousFamilyNameKey,
                                       CNContactNameSuffixKey,
                                       CNContactNicknameKey,
                                       CNContactOrganizationNameKey,
                                       CNContactDepartmentNameKey,
                                       CNContactJobTitleKey,
                                       CNContactPhoneticGivenNameKey,
                                       CNContactPhoneticMiddleNameKey,
                                       CNContactPhoneticFamilyNameKey,
#ifdef __IPHONE_10_0 // iOS系统版本 >= 10.0
                                       CNContactPhoneticOrganizationNameKey,
#endif
                                       CNContactBirthdayKey,
                                       CNContactNonGregorianBirthdayKey,
                                       CNContactNoteKey,
                                       CNContactImageDataKey,
                                       CNContactThumbnailImageDataKey,
                                       CNContactImageDataAvailableKey,
                                       CNContactTypeKey,
                                       CNContactPhoneNumbersKey,
                                       CNContactEmailAddressesKey,
                                       CNContactPostalAddressesKey,
                                       CNContactDatesKey,
                                       CNContactUrlAddressesKey,
                                       CNContactRelationsKey,
                                       CNContactSocialProfilesKey,
                                       CNContactInstantMessageAddressesKey,
                                       CNContactViewController.descriptorForRequiredKeys
                                       ]];
    
    CNContactFetchRequest * request = [[CNContactFetchRequest alloc]initWithKeysToFetch:keysToFetch];
    BOOL success = [contactStore enumerateContactsWithFetchRequest:request error:&contactError usingBlock:^(CNContact * __nonnull contact, BOOL * __nonnull stop){
        //
        if ([contact.identifier isEqualToString:identifier]) {
            findContact = contact;
            *stop = YES;
        }
    }];
    
    return findContact;
}

-(NSDictionary*) contactToDictionary:(CNContact *) person
                      withThumbnails:(BOOL)withThumbnails
{
    NSMutableDictionary* output = [NSMutableDictionary dictionary];

    NSString *recordID = person.identifier;
    NSString *givenName = person.givenName;
    NSString *familyName = person.familyName;
    NSString *middleName = person.middleName;
    NSString *company = person.organizationName;
    NSString *jobTitle = person.jobTitle;

    [output setObject:recordID forKey: @"recordID"];

    if (givenName) {
        [output setObject: (givenName) ? givenName : @"" forKey:@"givenName"];
    }

    if (familyName) {
        [output setObject: (familyName) ? familyName : @"" forKey:@"familyName"];
    }

    if(middleName){
        [output setObject: (middleName) ? middleName : @"" forKey:@"middleName"];
    }

    if(company){
        [output setObject: (company) ? company : @"" forKey:@"company"];
    }

    if(jobTitle){
        [output setObject: (jobTitle) ? jobTitle : @"" forKey:@"jobTitle"];
    }

    //handle phone numbers
    NSMutableArray *phoneNumbers = [[NSMutableArray alloc] init];

    for (CNLabeledValue<CNPhoneNumber*>* labeledValue in person.phoneNumbers) {
        NSMutableDictionary* phone = [NSMutableDictionary dictionary];
        NSString * label = [CNLabeledValue localizedStringForLabel:[labeledValue label]];
        NSString* value = [[labeledValue value] stringValue];

        if(value) {
            if(!label) {
                label = [CNLabeledValue localizedStringForLabel:@"other"];
            }
            [phone setObject: value forKey:@"number"];
            [phone setObject: label forKey:@"label"];
            [phoneNumbers addObject:phone];
        }
    }

    [output setObject: phoneNumbers forKey:@"phoneNumbers"];
    //end phone numbers

    //handle emails
    NSMutableArray *emailAddreses = [[NSMutableArray alloc] init];

    for (CNLabeledValue<NSString*>* labeledValue in person.emailAddresses) {
        NSMutableDictionary* email = [NSMutableDictionary dictionary];
        NSString* label = [CNLabeledValue localizedStringForLabel:[labeledValue label]];
        NSString* value = [labeledValue value];

        if(value) {
            if(!label) {
                label = [CNLabeledValue localizedStringForLabel:@"other"];
            }
            [email setObject: value forKey:@"email"];
            [email setObject: label forKey:@"label"];
            [emailAddreses addObject:email];
        } else {
            NSLog(@"ignoring blank email");
        }
    }

    [output setObject: emailAddreses forKey:@"emailAddresses"];
    //end emails

    //handle postal addresses
    NSMutableArray *postalAddresses = [[NSMutableArray alloc] init];

    for (CNLabeledValue<CNPostalAddress*>* labeledValue in person.postalAddresses) {
        CNPostalAddress* postalAddress = labeledValue.value;
        NSMutableDictionary* address = [NSMutableDictionary dictionary];

        NSString* street = postalAddress.street;
        if(street){
            [address setObject:street forKey:@"street"];
        }
        NSString* city = postalAddress.city;
        if(city){
            [address setObject:city forKey:@"city"];
        }
        NSString* state = postalAddress.state;
        if(state){
            [address setObject:state forKey:@"state"];
        }
        NSString* region = postalAddress.state;
        if(region){
            [address setObject:region forKey:@"region"];
        }
        NSString* postCode = postalAddress.postalCode;
        if(postCode){
            [address setObject:postCode forKey:@"postCode"];
        }
        NSString* country = postalAddress.country;
        if(country){
            [address setObject:country forKey:@"country"];
        }

        NSString* label = [CNLabeledValue localizedStringForLabel:labeledValue.label];
        if(label) {
            [address setObject:label forKey:@"label"];

            [postalAddresses addObject:address];
        }
    }

    [output setObject:postalAddresses forKey:@"postalAddresses"];
    //end postal addresses

    [output setValue:[NSNumber numberWithBool:person.imageDataAvailable] forKey:@"hasThumbnail"];
    if (withThumbnails) {
        [output setObject:[self getFilePathForThumbnailImage:person recordID:recordID] forKey:@"thumbnailPath"];
    }

    return output;
}

-(NSDictionary*) contactABRecordRefToDictionary:(ABRecordRef) person
                      withThumbnails:(BOOL)withThumbnails
{
    
    NSMutableDictionary* output = [NSMutableDictionary dictionary];
    
    NSString *recordID, *givenName, *familyName, *middleName, *company, *jobTitle;
    ABRecordID tempRecordID = ABRecordGetRecordID(person);
    recordID = [NSString stringWithFormat:@"%d", tempRecordID];
    givenName = (__bridge NSString *) ABRecordCopyValue(person, kABPersonFirstNameProperty);
    middleName = (__bridge NSString *) ABRecordCopyValue(person, kABPersonMiddleNameProperty);
    familyName = (__bridge NSString *) ABRecordCopyValue(person, kABPersonLastNameProperty);
    company = (__bridge NSString *) ABRecordCopyValue(person, kABPersonOrganizationProperty);
    jobTitle = (__bridge NSString *) ABRecordCopyValue(person, kABPersonJobTitleProperty);
    
    [output setObject:recordID forKey: @"recordID"];
    
    if (givenName) {
        [output setObject: (givenName) ? givenName : @"" forKey:@"givenName"];
    }
    
    if (familyName) {
        [output setObject: (familyName) ? familyName : @"" forKey:@"familyName"];
    }
    
    if(middleName){
        [output setObject: (middleName) ? middleName : @"" forKey:@"middleName"];
    }
    
    if(company){
        [output setObject: (company) ? company : @"" forKey:@"company"];
    }
    
    if(jobTitle){
        [output setObject: (jobTitle) ? jobTitle : @"" forKey:@"jobTitle"];
    }
    
    //handle phone numbers
    NSMutableArray *phoneNumbers = [[NSMutableArray alloc] init];
    // 复制
    ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
    // 取所有值 （）
    CFArrayRef array = ABMultiValueCopyArrayOfAllValues(phones);
    NSArray *tempPhoneNumbers = (__bridge NSArray *)array;
    // 值的 标题（浙江 杭州 移动）
    // CFStringRef str = ABMultiValueCopyLabelAtIndex(phones, 0);
    
    for(int index = 0; index< [tempPhoneNumbers count]; index++) {
        NSMutableDictionary* phone = [NSMutableDictionary dictionary];
        NSString *label = (__bridge NSString *)ABAddressBookCopyLocalizedLabel((ABMultiValueCopyLabelAtIndex(phones, index)));
        NSString *value = [tempPhoneNumbers objectAtIndex:index];
        
        if(value) {
            [phone setObject: value forKey:@"number"];
            if (label) {
                [phone setObject: label forKey:@"label"];
            }
            [phoneNumbers addObject:phone];
        }
    }
    
    [output setObject: phoneNumbers forKey:@"phoneNumbers"];
    //end phone numbers
    
    //handle emails
    NSMutableArray *emailAddresses = [[NSMutableArray alloc] init];
    ABMultiValueRef emailMultiValue = ABRecordCopyValue(person, kABPersonEmailProperty);
    NSArray *tempEmailAddresses = (__bridge NSArray *)ABMultiValueCopyArrayOfAllValues(emailMultiValue);
    
    for(int index = 0; index< [tempEmailAddresses count]; index++) {
        NSMutableDictionary* email = [NSMutableDictionary dictionary];
        NSString *label = (__bridge NSString *)ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(emailMultiValue, index));
        NSString *value = [tempEmailAddresses objectAtIndex:index];
        
        if(value) {
            [email setObject: value forKey:@"email"];
            if (label) {
                [email setObject: label forKey:@"label"];
            }
            [emailAddresses addObject:email];
        } else {
            NSLog(@"ignoring blank email");
        }
    }
    
    [output setObject: emailAddresses forKey:@"emailAddresses"];
    //end emails
    
    //handle postal addresses
    NSMutableArray *postalAddresses = [[NSMutableArray alloc] init];
    ABMultiValueRef postalMultiValue = ABRecordCopyValue(person, kABPersonAddressProperty);
    NSArray *tempPostalAddresses = (__bridge NSArray *)ABMultiValueCopyArrayOfAllValues(postalMultiValue);
    
    for(int index = 0; index< [tempPostalAddresses count]; index++) {
        NSMutableDictionary* address = [NSMutableDictionary dictionary];
        NSString *label = (__bridge NSString *)ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(postalMultiValue, index));
        //NSDictionary *value = [tempPostalAddresses objectAtIndex:index];
        NSDictionary *postalAddressDict = [tempPostalAddresses objectAtIndex:index];
        NSString* street = postalAddressDict[@"Street"];
        if(street){
            [address setObject:street forKey:@"street"];
        }
        NSString* city = postalAddressDict[@"City"];
        if(city){
            [address setObject:city forKey:@"city"];
        }
        NSString* state = postalAddressDict[@"State"];
        if(state){
            [address setObject:state forKey:@"state"];
        }
        NSString* region = postalAddressDict[@"Region"];
        if(region){
            [address setObject:region forKey:@"region"];
        }
        NSString* postCode = postalAddressDict[@"ZIP"];
        if(postCode){
            [address setObject:postCode forKey:@"postCode"];
        }
        NSString* country = postalAddressDict[@"Country"];
        if(country){
            [address setObject:country forKey:@"country"];
        }
        NSString* countryCode = postalAddressDict[@"CountryCode"];
        if(countryCode){
            [address setObject:countryCode forKey:@"countryCode"];
        }
        
        if(label) {
            [address setObject:label forKey:@"label"];
        }
        [postalAddresses addObject:address];
    }
    
    [output setObject:postalAddresses forKey:@"postalAddresses"];
    //end postal addresses
    
    [output setValue:[NSNumber numberWithBool:ABPersonHasImageData(person)] forKey:@"hasThumbnail"];
    if (withThumbnails) {
        [output setObject:[self getABRecordRefFilePathForThumbnailImage:person recordID:recordID] forKey:@"thumbnailPath"];
    }
    
    return output;
}

- (NSString *)thumbnailFilePath:(NSString *)recordID
{
    NSString *filename = [recordID stringByReplacingOccurrencesOfString:@":ABPerson" withString:@""];
    NSString* filepath = [NSString stringWithFormat:@"%@/rncontacts_%@.png", [self getPathForDirectory:NSCachesDirectory], filename];
    return filepath;
}

-(NSString *) getFilePathForThumbnailImage:(CNContact*) contact recordID:(NSString*) recordID
{
    NSString *filepath = [self thumbnailFilePath:recordID];
    
    BOOL fileExist = [[NSFileManager defaultManager] fileExistsAtPath:filepath];
    NSData *cachedContactImageData = [NSData dataWithContentsOfFile:filepath];
    
    if (contact.imageDataAvailable){
        NSData *contactImageData = contact.thumbnailImageData;
        if (contactImageData.length != cachedContactImageData.length) {
            BOOL success = [[NSFileManager defaultManager] createFileAtPath:filepath contents:contactImageData attributes:nil];
            
            if (!success) {
                NSLog(@"Unable to copy image");
            }
        }
    }

    if (fileExist) {
        return filepath;
    }

    return @"";
}

-(NSString *) getABRecordRefFilePathForThumbnailImage:(ABRecordRef)person recordID:(NSString*) recordID
{
    NSString *filepath = [self thumbnailFilePath:recordID];
    
    BOOL fileExist = [[NSFileManager defaultManager] fileExistsAtPath:filepath];
    NSData *cachedContactImageData = [NSData dataWithContentsOfFile:filepath];
    
    if (ABPersonHasImageData(person)){
        NSData *contactImageData = (__bridge NSData *)(ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail));
        if (contactImageData.length != cachedContactImageData.length) {
            BOOL success = [[NSFileManager defaultManager] createFileAtPath:filepath contents:contactImageData attributes:nil];
            
            if (!success) {
                NSLog(@"Unable to copy image");
            }
        }
    }
    
    if (fileExist) {
        return filepath;
    }
    
    return @"";
}

- (NSString *)getPathForDirectory:(int)directory
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES);
    return [paths firstObject];
}

RCT_EXPORT_METHOD(getPhotoForId:(nonnull NSString *)recordID callback:(RCTResponseSenderBlock)callback)
{
    if([CNContact class]) {
        //iOS 9+
        CNContactStore* contactStore = [self contactsStore:callback];
        if(!contactStore)
            return;
        
        CNEntityType entityType = CNEntityTypeContacts;
        if([CNContactStore authorizationStatusForEntityType:entityType] == CNAuthorizationStatusNotDetermined)
        {
            [contactStore requestAccessForEntityType:entityType completionHandler:^(BOOL granted, NSError * _Nullable error) {
                if(granted){
                    callback(@[[NSNull null], [self getFilePathForThumbnailImage:recordID addressBook:contactStore]]);
                }
            }];
        }
        else if( [CNContactStore authorizationStatusForEntityType:entityType]== CNAuthorizationStatusAuthorized)
        {
            callback(@[[NSNull null], [self getFilePathForThumbnailImage:recordID addressBook:contactStore]]);
        }
    } else {
        //iOS 8 and below
        ABAddressBookRef tempBookRef = [self bookRef:callback];
        if(!tempBookRef)
            return;
        
        CNEntityType entityType = CNEntityTypeContacts;
        if(ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
        {
            ABAddressBookRequestAccessWithCompletion(tempBookRef, ^(bool granted, CFErrorRef error) {
                if(granted){
                    callback(@[[NSNull null], [self getFilePathForThumbnailImage:recordID ABRecordRefAddressBook:tempBookRef]]);
                }
            });
        }
        else if(ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized)
        {
            callback(@[[NSNull null], [self getFilePathForThumbnailImage:recordID ABRecordRefAddressBook:tempBookRef]]);
        }
    }
}

-(NSString *) getFilePathForThumbnailImage:(NSString *)recordID
                               addressBook:(CNContactStore*)addressBook
{
    NSString *filepath = [self thumbnailFilePath:recordID];

    if([[NSFileManager defaultManager] fileExistsAtPath:filepath]) {
        return filepath;
    }

    NSError* contactError;
    NSArray * keysToFetch =@[CNContactThumbnailImageDataKey, CNContactImageDataAvailableKey];
    CNContact* contact = [addressBook unifiedContactWithIdentifier:recordID keysToFetch:keysToFetch error:&contactError];

    return [self getFilePathForThumbnailImage:contact recordID:recordID];
}

-(NSString *) getFilePathForThumbnailImage:(NSString *)recordID
                               ABRecordRefAddressBook:(ABAddressBookRef)addressBook
{
    NSString *filepath = [self thumbnailFilePath:recordID];
    
    if([[NSFileManager defaultManager] fileExistsAtPath:filepath]) {
        return filepath;
    }
    
    ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressBook, (ABRecordID)recordID);
    
    return [self getABRecordRefFilePathForThumbnailImage:person recordID:recordID];
}

RCT_EXPORT_METHOD(addContact:(NSDictionary *)contactData callback:(RCTResponseSenderBlock)callback)
{
    if([CNContact class]) {
        //iOS 9+
        CNContactStore* contactStore = [self contactsStore:callback];
        if(!contactStore)
            return;
        
        CNMutableContact * contact = [[CNMutableContact alloc] init];
        
        [self updateRecord:contact withData:contactData];
        
        @try {
            CNSaveRequest *request = [[CNSaveRequest alloc] init];
            [request addContact:contact toContainerWithIdentifier:nil];
            
            [contactStore executeSaveRequest:request error:nil];
            
            NSDictionary *contactDict = [self contactToDictionary:contact withThumbnails:false];
            
            callback(@[[NSNull null], contactDict]);
        }
        @catch (NSException *exception) {
            callback(@[[exception description], [NSNull null]]);
        }
    } else {
        //iOS 8 and below
        ABAddressBookRef tempBookRef = [self bookRef:callback];
        if(!tempBookRef)
            return;
        ABRecordRef person = ABPersonCreate();
        [self updateABRecordRef:person withData:contactData];
        
        @try {
            //ABPersonCreateInSource(person);
            
            //添加联系人
            ABAddressBookAddRecord(tempBookRef, person, NULL);
            
            //保存
            ABAddressBookSave(tempBookRef, NULL);
            
            NSDictionary *contactDict = [self contactABRecordRefToDictionary:person withThumbnails:false];
            
            //释放
            // CFRelease(person);
            
            callback(@[[NSNull null], contactDict]);
        }
        @catch (NSException *exception) {
            callback(@[[exception description], [NSNull null]]);
        }
    }
}

RCT_EXPORT_METHOD(openContactForm:(NSDictionary *)contactData callback:(RCTResponseSenderBlock)callback)
{
    if([CNContact class]) {
        //iOS 9+
        CNContactStore* contactStore = [self contactsStore:callback];
        if(!contactStore)
            return;
        
        self.callBack = callback;
        
        CNMutableContact * contact = [[CNMutableContact alloc] init];
        
        [self updateRecord:contact withData:contactData];
        
        CNContactViewController *controller = [CNContactViewController viewControllerForNewContact:contact];
        
        controller.delegate = self;
        
        dispatch_async(dispatch_get_main_queue(), ^{
            UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:controller];
            UINavigationController *viewController = (UINavigationController*)[[[[UIApplication sharedApplication] delegate] window] rootViewController];
            [viewController presentViewController:navigation animated:YES completion:nil];
        });
    } else {
        //iOS 8 and below
        ABAddressBookRef tempBookRef = [self bookRef:callback];
        if(!tempBookRef)
            return;
        
        self.callBack = callback;
        
        ABRecordRef person = ABPersonCreate();
        [self updateABRecordRef:person withData:contactData];
        
        [self showNewPersonView:person];
    }
}

RCT_EXPORT_METHOD(editContactForm:(NSDictionary *)contactData callback:(RCTResponseSenderBlock)callback)
{
    if([CNContact class]) {
        //iOS 9+
        CNContactStore* contactStore = [self contactsStore:callback];
        if(!contactStore)
            return;
        
        self.callBack = callback;
        
        CNMutableContact * contact = [[CNMutableContact alloc] init];
        //    CNMutableContact * contact = [[self getContactFromAddressBook:contactStore identifier:contactData[@"recordID"]] mutableCopy];
        
        [self updateRecord:contact withData:contactData];
        
        CNContactViewController *controller = [CNContactViewController viewControllerForContact:contact];
        [controller setAllowsEditing:YES];
        [controller setEditing:YES animated:YES];
        
        controller.delegate = self;
        
        dispatch_async(dispatch_get_main_queue(), ^{
            UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:controller];
            UINavigationController *viewController = (UINavigationController*)[[[[UIApplication sharedApplication] delegate] window] rootViewController];
            [viewController presentViewController:navigation animated:YES completion:nil];
        });
    } else {
        //iOS 8 and below
        ABAddressBookRef tempBookRef = [self bookRef:callback];
        if(!tempBookRef)
            return;
        
        self.callBack = callback;
        
        ABRecordRef person = ABAddressBookGetPersonWithRecordID(tempBookRef, [contactData[@"recordID"] intValue]);
        if (!person) {
            person = ABPersonCreate();
        }
        [self updateABRecordRef:person withData:contactData];
        
        [self showNewPersonView:person];
    }
}

#pragma mark - CNContactViewControllerDelegate
//dismiss open contact page after done or cancel is clicked
- (void)contactViewController:(CNContactViewController *)viewController didCompleteWithContact:(CNContact *)contact {
    [viewController dismissViewControllerAnimated:YES completion:nil];
    
    if (self.callBack) {
        if (contact) {
            NSDictionary *contactDict = [self contactToDictionary:contact withThumbnails:false];
            self.callBack(@[[NSNull null], contactDict]);
        } else {
            self.callBack(@[@"canceled", [NSNull null]]);
        }
    }
}

RCT_EXPORT_METHOD(updateContact:(NSDictionary *)contactData callback:(RCTResponseSenderBlock)callback)
{
    if([CNContact class]) {
        //iOS 9+
        CNContactStore* contactStore = [self contactsStore:callback];
        if(!contactStore)
            return;
        
        NSError* contactError;
        NSString* recordID = [contactData valueForKey:@"recordID"];
        NSArray * keysToFetch =@[
                                 CNContactEmailAddressesKey,
                                 CNContactPhoneNumbersKey,
                                 CNContactFamilyNameKey,
                                 CNContactGivenNameKey,
                                 CNContactMiddleNameKey,
                                 CNContactPostalAddressesKey,
                                 CNContactOrganizationNameKey,
                                 CNContactJobTitleKey,
                                 CNContactImageDataAvailableKey,
                                 CNContactThumbnailImageDataKey,
                                 CNContactImageDataKey
                                 ];
        
        @try {
            CNMutableContact* record = [[contactStore unifiedContactWithIdentifier:recordID keysToFetch:keysToFetch error:&contactError] mutableCopy];
            [self updateRecord:record withData:contactData];
            CNSaveRequest *request = [[CNSaveRequest alloc] init];
            [request updateContact:record];
            
            [contactStore executeSaveRequest:request error:nil];
            
            NSDictionary *contactDict = [self contactToDictionary:record withThumbnails:false];
            
            callback(@[[NSNull null], contactDict]);
        }
        @catch (NSException *exception) {
            callback(@[[exception description], [NSNull null]]);
        }
    } else {
        //iOS 8 and below
        ABAddressBookRef tempBookRef = [self bookRef:callback];
        if(!tempBookRef)
            return;
        
        NSString* recordID = [contactData valueForKey:@"recordID"];
        ABRecordRef person = ABAddressBookGetPersonWithRecordID(tempBookRef,(ABRecordID)recordID);//recordID为具体的数值,如3等,返回的肯定是有数据的
        
        @try {
            [self updateABRecordRef:person withData:contactData];
            ABAddressBookSave(tempBookRef, NULL);
            
            NSDictionary *contactDict = [self contactABRecordRefToDictionary:person withThumbnails:false];
            // 释放
            // CFRelease(person);
            
            callback(@[[NSNull null], contactDict]);
        }
        @catch (NSException *exception) {
            callback(@[[exception description], [NSNull null]]);
        }
    }
}

-(void) updateRecord:(CNMutableContact *)contact withData:(NSDictionary *)contactData
{
    NSString *givenName = [contactData valueForKey:@"givenName"];
    NSString *familyName = [contactData valueForKey:@"familyName"];
    NSString *middleName = [contactData valueForKey:@"middleName"];
    NSString *company = [contactData valueForKey:@"company"];
    NSString *jobTitle = [contactData valueForKey:@"jobTitle"];

    contact.givenName = givenName;
    contact.familyName = familyName;
    contact.middleName = middleName;
    contact.organizationName = company;
    contact.jobTitle = jobTitle;

    NSMutableArray *phoneNumbers = [[NSMutableArray alloc]init];

    for (id phoneData in [contactData valueForKey:@"phoneNumbers"]) {
        NSString *label = [phoneData valueForKey:@"label"];
        NSString *number = [phoneData valueForKey:@"number"];

        CNLabeledValue *phone;
        if ([label isEqual: @"main"]){
            phone = [[CNLabeledValue alloc] initWithLabel:CNLabelPhoneNumberMain value:[[CNPhoneNumber alloc] initWithStringValue:number]];
        }
        else if ([label isEqual: @"mobile"]){
            phone = [[CNLabeledValue alloc] initWithLabel:CNLabelPhoneNumberMobile value:[[CNPhoneNumber alloc] initWithStringValue:number]];
        }
        else if ([label isEqual: @"iPhone"]){
            phone = [[CNLabeledValue alloc] initWithLabel:CNLabelPhoneNumberiPhone value:[[CNPhoneNumber alloc] initWithStringValue:number]];
        }
        else{
            phone = [[CNLabeledValue alloc] initWithLabel:label value:[[CNPhoneNumber alloc] initWithStringValue:number]];
        }

        [phoneNumbers addObject:phone];
    }
    contact.phoneNumbers = phoneNumbers;

    NSMutableArray *emails = [[NSMutableArray alloc]init];

    for (id emailData in [contactData valueForKey:@"emailAddresses"]) {
        NSString *label = [emailData valueForKey:@"label"];
        NSString *email = [emailData valueForKey:@"email"];

        if(label && email) {
            [emails addObject:[[CNLabeledValue alloc] initWithLabel:label value:email]];
        }
    }

    contact.emailAddresses = emails;

    NSMutableArray *postalAddresses = [[NSMutableArray alloc]init];

    for (id addressData in [contactData valueForKey:@"postalAddresses"]) {
        NSString *label = [addressData valueForKey:@"label"];
        NSString *street = [addressData valueForKey:@"street"];
        NSString *postalCode = [addressData valueForKey:@"postalCode"];
        NSString *city = [addressData valueForKey:@"city"];
        NSString *country = [addressData valueForKey:@"country"];
        NSString *state = [addressData valueForKey:@"state"];

        if(label && street) {
            CNMutablePostalAddress *postalAddr = [[CNMutablePostalAddress alloc] init];
            postalAddr.street = street;
            postalAddr.postalCode = postalCode;
            postalAddr.city = city;
            postalAddr.country = country;
            postalAddr.state = state;
            [postalAddresses addObject:[[CNLabeledValue alloc] initWithLabel:label value: postalAddr]];
        }
    }

    contact.postalAddresses = postalAddresses;

    NSString *thumbnailPath = [contactData valueForKey:@"thumbnailPath"];

    if(thumbnailPath.length > 0 && [thumbnailPath rangeOfString:@"rncontacts_"].location == NSNotFound) {
        contact.imageData = [RCTContacts imageData:thumbnailPath];
    }
}

-(void) updateABRecordRef:(ABRecordRef)person withData:(NSDictionary *)contactData
{
    NSString *givenName = [contactData valueForKey:@"givenName"];
    NSString *familyName = [contactData valueForKey:@"familyName"];
    NSString *middleName = [contactData valueForKey:@"middleName"];
    NSString *company = [contactData valueForKey:@"company"];
    NSString *jobTitle = [contactData valueForKey:@"jobTitle"];
    
    ABRecordSetValue(person, kABPersonFirstNameProperty, (__bridge CFTypeRef)(givenName), NULL);
    ABRecordSetValue(person, kABPersonLastNameProperty, (__bridge CFTypeRef)(familyName), NULL);
    ABRecordSetValue(person, kABPersonMiddleNameProperty, (__bridge CFTypeRef)(middleName), NULL);
    ABRecordSetValue(person, kABPersonOrganizationProperty, (__bridge CFTypeRef)(company), NULL);
    ABRecordSetValue(person, kABPersonJobTitleProperty, (__bridge CFTypeRef)(jobTitle), NULL);
    
    //ABMutableMultiValueRef muRef = ABMultiValueCreateMutable(kABStringPropertyType);
    //ABMutableMultiValueRef muRefCopy = ABMultiValueCreateMutableCopy(phones);
    ABMutableMultiValueRef multiValuePhones = ABMultiValueCreateMutable(kABStringPropertyType);
    
    for (id phoneData in [contactData valueForKey:@"phoneNumbers"]) {
        NSString *label = [phoneData valueForKey:@"label"];
        NSString *number = [phoneData valueForKey:@"number"];

        if ([label isEqual: @"main"]){
            ABMultiValueAddValueAndLabel(multiValuePhones, (__bridge CFTypeRef)(number), kABPersonPhoneMainLabel, NULL);
        }
        else if ([label isEqual: @"mobile"]){
            ABMultiValueAddValueAndLabel(multiValuePhones, (__bridge CFTypeRef)(number), kABPersonPhoneMobileLabel, NULL);
        }
        else if ([label isEqual: @"iPhone"]){
            ABMultiValueAddValueAndLabel(multiValuePhones, (__bridge CFTypeRef)(number), kABPersonPhoneIPhoneLabel, NULL);
        }
        else{
            ABMultiValueAddValueAndLabel(multiValuePhones, (__bridge CFTypeRef)(number), (__bridge CFStringRef)(label), NULL);
        }
    }

    ABRecordSetValue(person, kABPersonPhoneProperty, multiValuePhones, NULL);
    CFRelease(multiValuePhones);
    
    ABMutableMultiValueRef multiValueEmails = ABMultiValueCreateMutable(kABStringPropertyType);

    for (id emailData in [contactData valueForKey:@"emailAddresses"]) {
        NSString *label = [emailData valueForKey:@"label"];
        NSString *email = [emailData valueForKey:@"email"];

        if(label && email) {
            ABMultiValueAddValueAndLabel(multiValueEmails, (__bridge CFTypeRef)(email), (__bridge CFStringRef)(label), NULL);
        }
    }

    ABRecordSetValue(person, kABPersonEmailProperty, multiValueEmails, NULL);
    CFRelease(multiValueEmails);
    
    ABMutableMultiValueRef multiValuePostalAddresses = ABMultiValueCreateMutable(kABDictionaryPropertyType);

    for (id addressData in [contactData valueForKey:@"postalAddresses"]) {
        ABMutableMultiValueRef multiValuePostalAddr = ABMultiValueCreateMutable(kABDictionaryPropertyType);
        
        NSString *label = [addressData valueForKey:@"label"];
        NSString *street = [addressData valueForKey:@"street"];
        NSString *postalCode = [addressData valueForKey:@"postalCode"];
        NSString *city = [addressData valueForKey:@"city"];
        NSString *country = [addressData valueForKey:@"country"];
        NSString *state = [addressData valueForKey:@"state"];

        if(label && street) {
            NSDictionary *values = [NSDictionary dictionaryWithObjectsAndKeys:
                                    (street), (NSString *)kABPersonAddressStreetKey,
                                    (postalCode), (NSString *)kABPersonAddressZIPKey,
                                    (city), (NSString *)kABPersonAddressCityKey,
                                    (state), (NSString *)kABPersonAddressStateKey,
                                    (country), (NSString *)kABPersonAddressCountryKey,
                                    nil];

            //
            ABMultiValueAddValueAndLabel(multiValuePostalAddresses, (__bridge CFDictionaryRef)values, (__bridge CFStringRef)(label), NULL);
        }
        
        CFRelease(multiValuePostalAddr);
    }

    ABRecordSetValue(person, kABPersonAddressProperty, multiValuePostalAddresses, NULL);
    CFRelease(multiValuePostalAddresses);
    
    NSString *thumbnailPath = [contactData valueForKey:@"thumbnailPath"];
    
    if(thumbnailPath.length > 0 && [thumbnailPath rangeOfString:@"rncontacts_"].location == NSNotFound) {
        ABPersonSetImageData(person, (__bridge CFDataRef)([RCTContacts imageData:thumbnailPath]), NULL);
    }
}

+ (NSData*) imageData:(NSString*)sourceUri
{
    if([sourceUri hasPrefix:@"assets-library"]){
        return [RCTContacts loadImageAsset:[NSURL URLWithString:sourceUri]];
    } else if ([sourceUri isAbsolutePath]) {
        return [NSData dataWithContentsOfFile:sourceUri];
    } else {
        return [NSData dataWithContentsOfURL:[NSURL URLWithString:sourceUri]];
    }
}

enum { WDASSETURL_PENDINGREADS = 1, WDASSETURL_ALLFINISHED = 0};

+ (NSData*) loadImageAsset:(NSURL*)assetURL {
    //thanks to http://www.codercowboy.com/code-synchronous-alassetlibrary-asset-existence-check/

    __block NSData *data = nil;
    __block NSConditionLock * albumReadLock = [[NSConditionLock alloc] initWithCondition:WDASSETURL_PENDINGREADS];
    //this *MUST* execute on a background thread, ALAssetLibrary tries to use the main thread and will hang if you're on the main thread.
    dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        ALAssetsLibrary * assetLibrary = [[ALAssetsLibrary alloc] init];
        [assetLibrary assetForURL:assetURL
                      resultBlock:^(ALAsset *asset) {
                          ALAssetRepresentation *rep = [asset defaultRepresentation];

                          Byte *buffer = (Byte*)malloc(rep.size);
                          NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:rep.size error:nil];
                          data = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];

                          [albumReadLock lock];
                          [albumReadLock unlockWithCondition:WDASSETURL_ALLFINISHED];
                      } failureBlock:^(NSError *error) {
                          NSLog(@"asset error: %@", [error localizedDescription]);

                          [albumReadLock lock];
                          [albumReadLock unlockWithCondition:WDASSETURL_ALLFINISHED];
                      }];
    });

    [albumReadLock lockWhenCondition:WDASSETURL_ALLFINISHED];
    [albumReadLock unlock];

    NSLog(@"asset lookup finished: %@ %@", [assetURL absoluteString], (data ? @"exists" : @"does not exist"));

    return data;
}

RCT_EXPORT_METHOD(deleteContact:(NSDictionary *)contactData callback:(RCTResponseSenderBlock)callback)
{
    if([CNContact class]) {
        //iOS 9+
        if(!contactStore) {
            contactStore = [[CNContactStore alloc] init];
        }
        
        NSString* recordID = [contactData valueForKey:@"recordID"];
        
        NSArray *keys = @[CNContactIdentifierKey];
        CNMutableContact *contact = [[contactStore unifiedContactWithIdentifier:recordID keysToFetch:keys error:nil] mutableCopy];
        NSError *error;
        CNSaveRequest *saveRequest = [[CNSaveRequest alloc] init];
        [saveRequest deleteContact:contact];
        [contactStore executeSaveRequest:saveRequest error:&error];
        
        callback(@[[NSNull null], [NSNull null]]);
    } else {
        //iOS 8 and below
        ABAddressBookRef tempBookRef = [self bookRef:callback];
        if(!tempBookRef)
            return;
        
        NSString* recordID = [contactData valueForKey:@"recordID"];
        ABRecordRef person = ABAddressBookGetPersonWithRecordID(tempBookRef,(ABRecordID)recordID);
        
        @try {
            ABAddressBookRemoveRecord(tempBookRef, person, NULL);
            ABAddressBookSave(tempBookRef, NULL);
            
            NSDictionary *contactDict = [self contactABRecordRefToDictionary:person withThumbnails:false];
            // 释放
            // CFRelease(person);
            
            callback(@[[NSNull null], contactDict]);
        }
        @catch (NSException *exception) {
            callback(@[[exception description], [NSNull null]]);
        }
    }
}

-(CNContactStore*) contactsStore: (RCTResponseSenderBlock)callback {
    if(!contactStore) {
        CNContactStore* store = [[CNContactStore alloc] init];

        if(!store.defaultContainerIdentifier) {
            NSLog(@"warn - no contact store container id");

            CNAuthorizationStatus authStatus = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
            if (authStatus == CNAuthorizationStatusDenied || authStatus == CNAuthorizationStatusRestricted){
                callback(@[@"denied", [NSNull null]]);
            } else {
                callback(@[@"undefined", [NSNull null]]);
            }

            return nil;
        }

        contactStore = store;
    }

    return contactStore;
}

-(ABAddressBookRef) bookRef: (RCTResponseSenderBlock)callback {
    if(!bookRef) {
        ABAddressBookRef temBookRef = ABAddressBookCreate();
        
        if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
            
            [self requestPermission:callback];
            
            return nil;
        }
        
        bookRef = temBookRef;
    }
    
    return bookRef;
}

+ (BOOL)requiresMainQueueSetup
{
    return YES;
}

//显示一个具体联系人的信息
- (void)showPersonView:(ABRecordRef)person editing:(BOOL)editing {
    //显示和编辑一个联系人信息
    ABPersonViewController *personVc = [[ABPersonViewController alloc]init];
//    ABRecordRef person = ABPersonCreate();
//    //CFString = CFSTR("")类似NSString = @"";直接赋值
//    ABRecordSetValue(person, kABPersonFirstNameProperty, CFSTR("jun"), NULL);
//    ABRecordSetValue(person, kABPersonLastNameProperty, CFSTR("ye"), NULL);
    personVc.personViewDelegate = self;
    personVc.displayedPerson = person;
    // CFRelease(person);
    personVc.allowsEditing = YES;
    //[personVc setEditing:editing animated:YES];
    // navigation
    dispatch_async(dispatch_get_main_queue(), ^{
        //把联系人信息作为导航栏的根视图，才可以显示编辑按钮和 完成按钮，返回按钮
        UINavigationController *navigation = [[UINavigationController alloc]initWithRootViewController:personVc];
        UINavigationController *viewController = (UINavigationController*)[[[[UIApplication sharedApplication] delegate] window] rootViewController];
        [viewController presentViewController:navigation animated:YES completion:nil];
        [navigation setEditing:editing animated:YES];
        [personVc setEditing:editing animated:YES];
    });
}
//完善一个联系人的信息
- (void)showUnknownPersonView:(ABRecordRef)person {
    //添加新联系人到通讯录，一般用于通讯录外面的应用，比如说点击某个按钮，直接进入联系人编辑，然后添加到通讯录
    ABUnknownPersonViewController *unknownPersonVc = [[ABUnknownPersonViewController alloc]init];
    unknownPersonVc.unknownPersonViewDelegate = self;
    unknownPersonVc.displayedPerson = person;
    // CFRelease(person);
    unknownPersonVc.allowsAddingToAddressBook = YES;//允许添加
    // navigation
    dispatch_async(dispatch_get_main_queue(), ^{
        UINavigationController *navigation = [[UINavigationController alloc]initWithRootViewController:unknownPersonVc];
        UINavigationController *viewController = (UINavigationController*)[[[[UIApplication sharedApplication] delegate] window] rootViewController];
        [viewController presentViewController:navigation animated:YES completion:nil];
    });
}
//增加一个新的联系人
- (void)showNewPersonView:(ABRecordRef)person {
    //新建联系人，允许用户填入一个新联系人的所有属性，一般是通讯录里面的新建
    ABNewPersonViewController *newPersonVc = [[ABNewPersonViewController alloc]init];
    newPersonVc.newPersonViewDelegate = self;
    newPersonVc.displayedPerson = person;
    // CFRelease(person);
    // navigation
    dispatch_async(dispatch_get_main_queue(), ^{
        UINavigationController *navigation = [[UINavigationController alloc]initWithRootViewController:newPersonVc];
        UINavigationController *viewController = (UINavigationController*)[[[[UIApplication sharedApplication] delegate] window] rootViewController];
        [viewController presentViewController:navigation animated:YES completion:nil];
    });
}

#pragma mark - ABPersonViewControllerDelegate
-(BOOL)personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
    return NO ;//不为默认操作
}

#pragma mark - ABUnknownPersonViewControllerDelegate
- (void)unknownPersonViewController:(ABUnknownPersonViewController *)unknownCardViewController didResolveToPerson:(nullable ABRecordRef)person
{
    //完善一个联系人的信息后做的事件，比如说模态退出，比如说不保存联系人到通讯录
    [unknownCardViewController dismissViewControllerAnimated:YES completion:nil];
    
    if (self.callBack) {
        if (person!=NULL) {
            NSDictionary *contactDict = [self contactABRecordRefToDictionary:person withThumbnails:false];
            self.callBack(@[[NSNull null], contactDict]);
        } else {
            self.callBack(@[@"canceled", [NSNull null]]);
        }
    }
}
-(BOOL)unknownPersonViewController:(ABUnknownPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
    return NO;
}

#pragma mark - ABNewPersonViewControllerDelegate
-(void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person
{
    //用于编辑新联系人后做的事件，比如说模态退出，比如说不保存联系人到通讯录
//    if (person!=NULL) {
//        ABAddressBookRef adbk=ABAddressBookCreate();
//        //从数据库中删除新加的联系人
//        ABAddressBookRemoveRecord(adbk, person, NULL);
//        CFStringRef name=ABRecordCopyCompositeName(person);
//        NSLog(@"name%@",name);//对新加的联系人做处理，不一定要保存到通讯录数据库
//        CFRelease(name);
//        CFRelease(adbk);
//    }
    [newPersonView dismissViewControllerAnimated:YES completion:nil];
    
    if (self.callBack) {
        if (person!=NULL) {
            NSDictionary *contactDict = [self contactABRecordRefToDictionary:person withThumbnails:false];
            self.callBack(@[[NSNull null], contactDict]);
        } else {
            self.callBack(@[@"canceled", [NSNull null]]);
        }
    }
}

#pragma mark - from RCTContactsWrapper
/* Get basic contact data as JS object */
RCT_EXPORT_METHOD(getContact:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
//    [self.contactsWrapper launchContactsContactResove:resolve rejecter:reject];
    [self.contactsWrapper launchContactsContactSelected:^(CNContact *result1, ABRecordRef result2) {
        //
        NSDictionary *contactDictionary = nil;
        if (result1) {
            contactDictionary = [self contactToDictionary:result1 withThumbnails:YES];
        } else if (result2) {
            contactDictionary = [self contactABRecordRefToDictionary:result2 withThumbnails:YES];
        }
        if (contactDictionary) {
            if (resolve) {
                resolve(contactDictionary);
            }
        } else if (!result1 && !result2) {
            if (reject) {
                reject(@"E_CONTACT_EXCEPTION", @"Canceled", nil);
            }
        }
    }];
    
    
}

/* Get ontact email as string */
RCT_EXPORT_METHOD(getEmail:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
    [self.contactsWrapper launchContactsEmailResove:resolve rejecter:reject];
    
    
}

@end
